package DB;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.exception.DataException;

/* This class holds and manage all the data in the tables 
 * the class contain references to the TagsTable and the FilesTable */

public class DataBase implements DBmanager
{
	private SessionFactory seFac;

	public DataBase(SessionFactory sessionFactory)
	{
		seFac = sessionFactory;
	}

	/**
	 * this function add new tag if it doesn't exist
	 * @param tag {@link String} the name of the tag
	 * @return {@link TagsTable} an instance of the new tag or null if it exist   
	 */
	public TagsTable addTag(String tag) 
	{
		Session se = seFac.openSession();
		TagsTable  tagT = null;
		Query query = se.createQuery("from TagsTable where tagName='"+tag+"'");
		if(!query.list().isEmpty())
		{
			List <TagsTable>list = query.list();
			Iterator<TagsTable> it=list.iterator();
			tagT = it.next();
		}
		if(tagT == null)
		{
			tagT= new TagsTable();
			tagT.setTagName(tag);
			se.save(tagT);
			se.flush();
			se.clear();
			se.close();
			return tagT;
		}
		else 
			return null; //tag already exist
	}
	
	/**
	 * this function connect between file and tag 
	 * @param fileName the name of the requested file by {@link String}
	 * @param tag the name of the tag by {@link String}
	 * @param path the path of the file by {@link String}
	 * @return {@link Boolean} true if the operation succeed or false if not
	 */
	public boolean addTagToFile(String fileName, String tag, String path)
	{
		Session se = seFac.openSession();
		Transaction tr = se.beginTransaction();
		
		TagsTable  tagT = null;
		Query query = se.createQuery("from TagsTable where tagName='"+tag+"'");
		if(!query.list().isEmpty())
		{
			List <TagsTable>list = query.list();
			Iterator<TagsTable> it=list.iterator();
			tagT = it.next();
		}
		if(tagT == null)
		{
			tagT = addTag(tag);
		}
		FilesTable  file = null;
		String sqlQuery="from FilesTable where location=:path"; 

		query = se.createQuery(sqlQuery).setParameter("path", path.toString());
		if(!query.list().isEmpty())
		{
			List <FilesTable>list = query.list();
			Iterator<FilesTable> it=list.iterator();
			file = it.next();
		}
		if(file != null)
		{
			if(!tagT.getFiles().contains(file))
			{
				tagT.getFiles().add(file);
				file.getTags().add(tagT);
				se.saveOrUpdate(tagT);
				se.update(file);
			}
			tr.commit();
		}
		else
		{
			file = new FilesTable();
			file.setLocation(path);
			file.setFileName(fileName);
			se.save(file);
			tagT.getFiles().add(file);
			file.getTags().add(tagT);
			tr.commit();
		}
		return true;
	}
	
	/**
	 * this function remove a tag from requested file
	 * @param tag the name of the tag by {@link String}
	 * @param path the path of the file by {@link String}
	 * @return {@link Boolean} true if the operation succeed or false if not
	 */
	public boolean delTagFromFile(String tag, String path) 
	{
		Session se = seFac.openSession();
		TagsTable  tagT = null;
		Query query = se.createQuery("from TagsTable where tagName='"+tag+"'");
		if(!query.list().isEmpty())
		{
			List <TagsTable>list = query.list();
			Iterator<TagsTable> it=list.iterator();
			tagT = it.next();
		}
		if(tagT == null)
		{
			se.close();
			return true;
		}
		FilesTable  file = null;
		String sqlQuery="from FilesTable where location=:path"; 

		query = se.createQuery(sqlQuery).setParameter("path", path.toString());
		if(!query.list().isEmpty())
		{
			List <FilesTable>list = query.list();
			Iterator<FilesTable> it=list.iterator();
			file = it.next();
		}
		if(file != null)
		{
			tagT.getFiles().remove(file); 
		}
		se.close();
		return false; //no such file
	}
	
	/**
	 * this function delete all the tags from the system
	 */
	public void delAllTags() 
	{
		Session se = seFac.openSession();
		Transaction tr = se.beginTransaction();
		Query query = se.createQuery("from TagsTable");
		if(!query.list().isEmpty())
		{
			List <TagsTable>list = query.list();
			Iterator<TagsTable> it=list.iterator();
			while(it.hasNext())
			{
				se.delete(it.next());
			}
		}
		tr.commit();
	}
	
	/**
	 * this function delete all the files from the system
	 */
	public void delAllFiles() 
	{
		Session se = seFac.openSession();
		Transaction tr = se.beginTransaction();
		Query query = se.createQuery("from FilesTable");
		if(!query.list().isEmpty())
		{
			List <FilesTable>list = query.list();
			Iterator<FilesTable> it=list.iterator();
			while(it.hasNext())
				se.delete(it.next());
		}
		tr.commit();
	}
	
	/**
	 * this function delete the requested tag from the system
	 * @param tag {@link String} the name of the tag
	 * @return {@link Boolean} true if the operation succeed or false if not
	 */
	public boolean deleteTag(String tag) 
	{
		TagsTable  tagT = null;
		Session se = seFac.openSession();
		Query query = se.createQuery("from TagsTable where tagName='"+tag+"'");
		if(!query.list().isEmpty())
		{
			List <TagsTable>list = query.list();
			Iterator<TagsTable> it=list.iterator();
			tagT = it.next();
		}
		if(tagT != null)
		{
			se.delete(tagT);
			se.flush();
			se.close();
			return true;
		}
		else
		{
			se.close();
			return false;
		}
	}

	/**
	 * this function get all the files that connected to the tag
	 * @param tag {@link String} the name of the tag
	 * @return {@link Set} of strings with the paths of all the files
	 */
	public Set<String> getFiles(String tag)   
	{
		TagsTable  tagT = null;
		Session se = seFac.openSession();
		Query query = se.createQuery("from TagsTable where tagName='"+tag+"'");
		if(!query.list().isEmpty())
		{
			List <TagsTable>list = query.list();
			Iterator<TagsTable> it=list.iterator();
			tagT = it.next();
		}
		Set<String> filesPath = null;
		if(tagT != null)
		{
			filesPath = new HashSet<String>();
			Set<FilesTable> files = tagT.getFiles();
			Iterator<FilesTable> it = files.iterator();
			while(it.hasNext())
				filesPath.add( it.next().getLocation());
		}
		se.close();
		return filesPath;
	}
	
	/**
	 * this function get all the tags that connected to the file
	 * @param path the path of the requested file by {@link String}
	 * @return {@link Set}  of strings with the names of all the tags
	 */
	public Set<String> getTags(String path) 
	{
		Session se = seFac.openSession();
		FilesTable  file = null;
		String sqlQuery="from FilesTable where location=:path"; 

		Query query = se.createQuery(sqlQuery).setParameter("path", path.toString());
		if(!query.list().isEmpty())
		{
			List <FilesTable>list = query.list();
			Iterator<FilesTable> it=list.iterator();
			file = it.next();
		}
		Set<String> tagsName = null;
		if(file != null)
		{
			tagsName = new HashSet<String>();
			Set<TagsTable> f = file.getTags();
			Iterator<TagsTable> it = f.iterator();
			while(it.hasNext())
				tagsName.add( it.next().getTagName());
		}
		se.close();
		return tagsName;
	}
	
	/**
	 * this function add new tag to the table (if it doesn't exist)
	 * @param file the name of the file by {@link String} 
	 * @param path the path of the requested file by {@link String}
	 * @return {@link Boolean} true if the operation succeed or false if not
	 */
	public boolean addFile(String file, String path) 
	{
		Session se = seFac.openSession();
		FilesTable  fileT = null;
		String sqlQuery="from FilesTable where location=:path"; 

		Query query = se.createQuery(sqlQuery).setParameter("path", path.toString());
		if(!query.list().isEmpty())
		{
			List <FilesTable>list = query.list();
			Iterator<FilesTable> it=list.iterator();
			fileT = it.next();
		}
		if(fileT == null)
		{
			fileT = new FilesTable();
			fileT.setFileName(file);
			fileT.setLocation(path);
			se.saveOrUpdate(fileT);
			se.flush();
			se.close();
			return true;
		}
		else 
		{
			se.close();
			return false;
		}
	}
	
	/**
	 * this function delete the requested file (if it's exist)
	 * @param path the path of the requested file by {@link String}
	 * @return {@link Boolean} true if the operation succeed or false if not
	 */
	public boolean deleteFile(String path) 
	{
		FilesTable fileT= null;
		Session se = seFac.openSession();
		path = path.replace("\\", "\\\\\\\\");
		path = path+"%";
		Query query = se.createQuery("from FilesTable where location like '"+path+"'");
		if(!query.list().isEmpty())
		{
			List <FilesTable>list = query.list();
			Iterator<FilesTable> itr=list.iterator();
			while(itr.hasNext())
			{
				fileT = itr.next();
				Set<TagsTable> f = fileT.getTags();
				Iterator<TagsTable> it = f.iterator();
				while(it.hasNext())
				{
					TagsTable t = it.next();
					t.getFiles().remove(fileT);
				}
				se.delete(fileT);
				se.flush();
			}
		}
		se.close();
		if(fileT != null)
			return true;
		return false;
	}
	
	/**
	 * this function return all the files in the system
	 * @return {@link Set} of strings with the paths of all the files
	 */
	public Set<String> getAllFiles() 
	{
		Session se = seFac.openSession();
		Set<String> filesPath = null;
		Query query = se.createQuery("from FilesTable");
		if(!query.list().isEmpty())
		{
			filesPath = new HashSet<String>();
			List <FilesTable>list = query.list();
			Iterator<FilesTable> it=list.iterator();
			while(it.hasNext())
				filesPath.add( it.next().getLocation());
		}
		se.close();
		return filesPath;
	}

	/**
	 * this function return all the tags in the system
	 * @return {@link Set} of strings with the paths of all the files
	 */
	public Set<String> getAllTags()   
	{
		Session se = seFac.openSession();
		Set<String> tags = new HashSet<String>();
		Query query = se.createQuery("from TagsTable");
		if(!query.list().isEmpty())
		{
			List <TagsTable> list = query.list();
			Iterator<TagsTable> it=list.iterator();
			while(it.hasNext())
			{
				tags.add( it.next().getTagName());
			}
			se.close();
			return tags;
		}
		se.close();
		return null;
	}
	
	/**
	 * this function return all the tags in the system
	 * @param YesTags {@link Set} of tags that the user wants the files that connect to them
	 * @param NoTags {@link Set} of tags that the user doesn't wants the files that connect to them
	 * @return {@link Set} of strings with the paths of all the requested files
	 */
	public Set<String> getFileByYesNoTags(Set<String> YesTags, Set<String> NoTags)    
	{
		Set<String> Nofiles = null;
		Set<String> Yesfiles = null;
		if(YesTags != null)
		{
			Yesfiles = new HashSet<String>();
			int flag = 0;
			Iterator<String> stIt = YesTags.iterator();
			Set<String> temp = null; 
			Yesfiles = getFiles(stIt.next());
			while(stIt.hasNext())
			{
				temp = getFiles(stIt.next());
				Yesfiles.retainAll(temp);
			}
		}
		else
			Yesfiles = getAllFiles();

		if( NoTags != null)
		{
			Nofiles = new HashSet<String>();
			Iterator<String> stIt = NoTags.iterator();
			Set<String> temp = null;
			while(stIt.hasNext())
			{
				temp = getFiles(stIt.next());
				if(temp != null)
					Nofiles.addAll(temp);
			}
			if (Yesfiles != null)
				Yesfiles.removeAll(Nofiles);
		}

		return Yesfiles;	
	}
	
	/**
	 * this function delete all the tags from the requested file
	 * @param path the path of the requested file by {@link String}
	 * @return {@link Boolean} true if the operation succeed or false if not
	 */
	public boolean removeAllTagFromFile(String path)
	{
		Session se = seFac.openSession();
		FilesTable  fileT = null;
		String sqlQuery="from FilesTable where location=:path"; 

		Query query = se.createQuery(sqlQuery).setParameter("path", path.toString());
		if(!query.list().isEmpty())
		{
			List <FilesTable>list = query.list();
			Iterator<FilesTable> it=list.iterator();
			fileT = it.next();
		}
		if(fileT != null)
		{
			Iterator<TagsTable> it = fileT.getTags().iterator();
			TagsTable t = null;
			while(it.hasNext())
			{
				t = it.next();
				t.getFiles().remove(fileT);
			}
			se.close();
			return true;
		}
		se.close();
		return false;
	}

}